package org.filecollector.filecontroller.virtualcatalogue.groupname;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.regex.Pattern;

/**
 * Name of group of files. It works like IP: mask and group ids that defines id diapasons
 * @apiNote this class has a natural ordering that is inconsistent with equals.
 */
@AllArgsConstructor
class GroupNameClass implements GroupName {
    @Getter public static final Pattern namePattern = Pattern.compile(
            "^([0-7]?[0-9A-F]{1,15})M((?>[1-5]?[0-9])|(?>6[0-4]))$"
    );
    private Long minId;
    private Long maxId;
    private byte mask;

    /**
     * Default constructor of GroupNameClass
     * @param id id of directory
     * @param mask mask of directory
     */
    public GroupNameClass(Long id, byte mask) {
        if (this.mask > 64 || this.mask < 0) {
            throw new RuntimeException("Incorrect mask");
        }
        this.mask = mask;
        if (this.mask >= 63) {
            minId = 0L;
            maxId = Long.MAX_VALUE;
            return;
        }
        long maskingBytes = (1L << (this.mask)) - 1;
        minId = (~maskingBytes) & id;
        maxId = (maskingBytes | id);
    }

    /**
     * Check if id matches with group name
     * @param id checking id
     * @return true if id matches; false if not
     */
    @Override
    public boolean matchesId(Long id) {
        return (minId <= id) && (id <= maxId);
    }

    /**
     * Return name of tracking catalogue name
     * @return name of tracking catalogue name
     */
    @Override
    public String getName() {
        return Long.toString(minId, 16) + "M" + mask;
    }

    @Override
    public boolean includes(GroupName another) {
        return minId < another.getMinId() && another.getMaxId() > maxId;
    }

    /**
     * @return name of tracking catalogue in JSON format
     */
    @Override
    public String toJSONElement() {
        return "\"" + getName() + "\"";
    }

    /**
     * @param name other group name
     * @return true if id ranges is intersected; false in other cases
     */
    @Override
    public boolean isIntersected(GroupName name) {
        return this.compareTo(name) == 0;
    }

    @Override
    public Long getMinId() {
        return minId;
    }

    @Override
    public Long getMaxId() {
        return maxId;
    }

    /**
     * check if string matches to name
     * @param name some tring
     * @return if name can be parsed
     */
    public static boolean matchesName(String name) {
        return namePattern.matcher(name).matches();
    }

    /**
     * Compare to other {@link GroupName}
     * @param groupName the object to be compared.
     * @return <li>1 if this name diapason is less than comparing one</li>
     * <li>-1 if this name diapason is bigger than comparing one</li>
     * <li>0 if ranges of values is intersecting</li>
     */
    @Override
    public int compareTo(GroupName groupName) {
        if (groupName.getMaxId() < getMinId()) {
            return 1;
        }
        if (groupName.getMinId() > getMaxId()) {
            return -1;
        }
        return 0;
    }

    @Override
    public boolean equals(Object another) {
        if (another == null) {
            return false;
        }
        if (!(another instanceof GroupNameClass)) {
            return false;
        }
        var anotherGroupName = (GroupNameClass) another;
        if (!anotherGroupName.minId.equals(this.minId)) {
            return false;
        }
        if (!anotherGroupName.maxId.equals(this.maxId)) {
            return false;
        }
        if (anotherGroupName.mask != this.mask) {
            return false;
        }
        return true;
    }
}

